#include <gtest/gtest.h>

#include "MyIterator.h"
#include <helper/iter/Take.h>
#include <helper/unittest/Tracer.h>

using namespace helper;
using namespace helper::iter;

TEST(TakeTest, testNext){
  //next
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(3).next().value());

  ASSERT_EQ(false, MyIterator<int>(0, 10).take(0).next().has_value());
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(1).next().value());
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(9).next().value());
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(10).next().value());
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(11).next().value());
}

TEST(TakeTest, testNth){
  //nth, based-0, expect=>first elem
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(3).nth(0).value());

  // take_n == 0;  no-elem
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(0).nth(0).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(0).nth(1).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(0).nth(8).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(0).nth(9).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(0).nth(10).has_value());


  // take_n <= size_n [1,10]; has_elems
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(3).nth(0).value());
  ASSERT_EQ(1, MyIterator<int>(0, 10).take(3).nth(1).value());
  ASSERT_EQ(2, MyIterator<int>(0, 10).take(3).nth(2).value());

  ASSERT_EQ(false, MyIterator<int>(0, 10).take(3).nth(4).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(3).nth(9).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(3).nth(10).has_value());

  // take_n > size_n; all elems
  ASSERT_EQ(0, MyIterator<int>(0, 10).take(11).nth(0).value());
  ASSERT_EQ(8, MyIterator<int>(0, 10).take(11).nth(8).value());
  ASSERT_EQ(9, MyIterator<int>(0, 10).take(11).nth(9).value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(11).nth(10).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(11).nth(11).has_value());
  ASSERT_EQ(false, MyIterator<int>(0, 10).take(11).nth(100).has_value());
}

TEST(TakeTest, testAdvanceBy){
  // take_n = 0;
  ASSERT_TRUE(MyIterator<int>(0, 10).take(0).advanceBy(0).isOK());
  ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).take(0).advanceBy(1).error());
  ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).take(0).advanceBy(3).error());

  // take_n = normal
  ASSERT_TRUE(MyIterator<int>(0, 10).take(3).advanceBy(0).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(3).advanceBy(1).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(3).advanceBy(2).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(3).advanceBy(3).isOK());
  ASSERT_EQ((size_t)3 ,MyIterator<int>(0, 10).take(3).advanceBy(4).error());
  ASSERT_EQ((size_t)3 ,MyIterator<int>(0, 10).take(3).advanceBy(5).error());
  ASSERT_EQ((size_t)3 ,MyIterator<int>(0, 10).take(3).advanceBy(9).error());
  ASSERT_EQ((size_t)3 ,MyIterator<int>(0, 10).take(3).advanceBy(10).error());
  ASSERT_EQ((size_t)3 ,MyIterator<int>(0, 10).take(3).advanceBy(11).error());

  // take_n > iter.n
  ASSERT_TRUE(MyIterator<int>(0, 10).take(11).advanceBy(0).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(11).advanceBy(1).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(11).advanceBy(2).isOK());

  ASSERT_TRUE(MyIterator<int>(0, 10).take(11).advanceBy(9).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(11).advanceBy(10).isOK());

  ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(11).advanceBy(11).error());
  ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(11).advanceBy(12).error());
  ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(11).advanceBy(13).error());

  //take_n == iter.n
  ASSERT_TRUE(MyIterator<int>(0, 10).take(10).advanceBy(0).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(10).advanceBy(1).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(10).advanceBy(2).isOK());

  ASSERT_TRUE(MyIterator<int>(0, 10).take(10).advanceBy(9).isOK());
  ASSERT_TRUE(MyIterator<int>(0, 10).take(10).advanceBy(10).isOK());

  ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(10).advanceBy(11).error());
  ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(10).advanceBy(12).error());
  ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(10).advanceBy(13).error());
}



TEST(TakeTest, testTryFlold__All){
  {
    bool ok = MyIterator<int>(0, 10).take(3)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::BREAK, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Continue() : ControlFlowFlag::Break();
          }).isContinue();
    ASSERT_TRUE(ok);

    ASSERT_TRUE(MyIterator<int>(0, 10).take(0).all([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(3).all([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(9).all([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(10).all([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(11).all([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(100).all([](const auto & v) { return v >=0; }));


    ASSERT_FALSE(MyIterator<int>(0, 10).take(0).any([](const auto & v) { return v >=0; }));

    ASSERT_TRUE(MyIterator<int>(0, 10).take(3).any([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(9).any([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(10).any([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(11).any([](const auto & v) { return v >=0; }));
    ASSERT_TRUE(MyIterator<int>(0, 10).take(100).any([](const auto & v) { return v >=0; }));
  }
}
TEST(TakeTest, testFold__Count){
  {
    EXPECT.reset()
    ;
    TRACER.reset();
    // take_n = 0
    MyIterator<int>(0, 10).take(0)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).take(0)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).take(0).count());
  }


  // take_n = normal
  {
    EXPECT.reset()
      .operation("[0,10).take(1)->v:0")
      ;
    TRACER.reset();
    MyIterator<int>(0, 10).take(1)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).take(1)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)1, MyIterator<int>(0, 10).take(1).count());
  }

  {
    EXPECT.reset()
      .operation("[0,10).take(2)->v:0")
      .operation("[0,10).take(2)->v:1")
      ;
    TRACER.reset();
    MyIterator<int>(0, 10).take(2)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).take(2)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)2, MyIterator<int>(0, 10).take(2).count());
  }

  {
    EXPECT.reset()
      .operation("[0,10).take(9)->v:0")
      .operation("[0,10).take(9)->v:1")
      .operation("[0,10).take(9)->v:2")
      .operation("[0,10).take(9)->v:3")
      .operation("[0,10).take(9)->v:4")
      .operation("[0,10).take(9)->v:5")
      .operation("[0,10).take(9)->v:6")
      .operation("[0,10).take(9)->v:7")
      .operation("[0,10).take(9)->v:8")
      ;
    TRACER.reset();
    MyIterator<int>(0, 10).take(9)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).take(9)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)9, MyIterator<int>(0, 10).take(9).count());
  }

  // take_n = iter.n
  {
    EXPECT.reset()
      .operation("[0,10).take(10)->v:0")
      .operation("[0,10).take(10)->v:1")
      .operation("[0,10).take(10)->v:2")
      .operation("[0,10).take(10)->v:3")
      .operation("[0,10).take(10)->v:4")
      .operation("[0,10).take(10)->v:5")
      .operation("[0,10).take(10)->v:6")
      .operation("[0,10).take(10)->v:7")
      .operation("[0,10).take(10)->v:8")
      .operation("[0,10).take(10)->v:9")
    ;
    TRACER.reset();
    MyIterator<int>(0, 10).take(10)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).take(10)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(10).count());
  }

  // take_n > iter.n
  {
    EXPECT.reset()
      .operation("[0,10).take(11)->v:0")
      .operation("[0,10).take(11)->v:1")
      .operation("[0,10).take(11)->v:2")
      .operation("[0,10).take(11)->v:3")
      .operation("[0,10).take(11)->v:4")
      .operation("[0,10).take(11)->v:5")
      .operation("[0,10).take(11)->v:6")
      .operation("[0,10).take(11)->v:7")
      .operation("[0,10).take(11)->v:8")
      .operation("[0,10).take(11)->v:9")
      ;
    TRACER.reset();
    MyIterator<int>(0, 10).take(11)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).take(11)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(11).count());
  }
  {
    EXPECT.reset()
      .operation("[0,10).take(100)->v:0")
      .operation("[0,10).take(100)->v:1")
      .operation("[0,10).take(100)->v:2")
      .operation("[0,10).take(100)->v:3")
      .operation("[0,10).take(100)->v:4")
      .operation("[0,10).take(100)->v:5")
      .operation("[0,10).take(100)->v:6")
      .operation("[0,10).take(100)->v:7")
      .operation("[0,10).take(100)->v:8")
      .operation("[0,10).take(100)->v:9")
      ;
    TRACER.reset();
    MyIterator<int>(0, 10).take(100)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).take(100)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).take(100).count());
  }
}
